VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "Queue"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Option Explicit


'This class utilizes a linked-list stack, since VB doesn't support a stack inherently.
'Since VB also doesn't support exceptions, and only numeric error codes, the stack
'will return nothing for pop and peek if no objects exist and a direct reference to the
'objects if they do exist.

Private vm_Front As LLNode
Private vm_Rear As LLNode
Private vm_numItems_int As Integer


Private Sub Class_Initialize()
resetObject
End Sub

Private Sub Class_Terminate()
resetObject
End Sub

Public Sub clear()
resetObject
End Sub

Friend Sub initWithFront(ByVal pr_FrontNode As LLNode)
Dim vl_CurrentNode As LLNode
Dim vl_PreviousNode As LLNode

Set vm_Front = pr_FrontNode
Set vl_CurrentNode = pr_FrontNode

Do Until vl_CurrentNode Is Nothing
    Set vl_PreviousNode = vl_CurrentNode
    Set vl_CurrentNode = vl_CurrentNode.nextItem
    vm_numItems_int = vm_numItems_int + 1
Loop

Set vm_Rear = vl_PreviousNode
End Sub

Private Sub resetObject()
Set vm_Front = Nothing
Set vm_Rear = Nothing
vm_numItems_int = 0
End Sub

Public Sub enqueue(ByVal pr_ObjectToAdd_obj As Variant)
Const CL_ERR_INVALIDPARAMETER As Integer = 91

Dim vl_NewNode As LLNode
Set vl_NewNode = New LLNode

If Not isVariantInitialized(pr_ObjectToAdd_obj) Then
    Err.Raise CL_ERR_INVALIDPARAMETER
End If

vl_NewNode.initWithData pr_ObjectToAdd_obj

If vm_numItems_int = 0 Then
    Set vm_Front = vl_NewNode
Else
    Set vm_Rear.nextItem = vl_NewNode
End If
Set vm_Rear = vl_NewNode


vm_numItems_int = vm_numItems_int + 1
End Sub

Private Sub checkForEmptyAndSetFrontRear()
If vm_numItems_int = 0 Then Set vm_Rear = Nothing: Set vm_Front = Nothing
End Sub

Public Function dequeue() As Variant
Dim vl_ReturnObject_obj As Variant

If vm_numItems_int = 0 Then
    Set vl_ReturnObject_obj = Nothing
Else
    assignVariantVariable vl_ReturnObject_obj, vm_Front.Data
    
    Set vm_Front = vm_Front.nextItem
    
    vm_numItems_int = vm_numItems_int - 1
    checkForEmptyAndSetFrontRear
End If

assignVariantVariable dequeue, vl_ReturnObject_obj
End Function

Public Function front() As Variant
Dim vl_ReturnObject_obj As Variant

If vm_numItems_int = 0 Then
    Set vl_ReturnObject_obj = Nothing
Else
    assignVariantVariable vl_ReturnObject_obj, vm_Front.Data
End If

assignVariantVariable front, vl_ReturnObject_obj
End Function

Friend Function frontNode() As LLNode
Set frontNode = vm_Front
End Function

Public Function rear() As Variant
Dim vl_ReturnObject As Variant

If vm_numItems_int = 0 Then
    Set vl_ReturnObject = Nothing
Else
    assignVariantVariable vl_ReturnObject, vm_Rear.Data
End If

assignVariantVariable rear, vl_ReturnObject
End Function

Public Property Get numItems() As Integer
numItems = vm_numItems_int
End Property

Public Function isQueueEmpty() As Boolean
isQueueEmpty = (vm_numItems_int = 0)
End Function


'This clone method only clones one item deep. It does not perform a true deep copy,
'only copies the nodes themselves. For nodes that contain objects, the objects are not copied.
Public Function clone() As Queue
Dim vl_Copy As Queue
Dim vl_NewFront As LLNode

Dim vl_NewQueueNode As LLNode
Dim vl_CurrentQueueNode As LLNode

If vm_numItems_int = 0 Then
    Set vl_Copy = New Queue
Else
    
    Set vl_NewQueueNode = vm_Front.cloneNoNext
    Set vl_NewFront = vl_NewQueueNode
    Set vl_CurrentQueueNode = vm_Front
    
    Do Until vl_CurrentQueueNode.nextItem Is Nothing
        Set vl_CurrentQueueNode = vl_CurrentQueueNode.nextItem
    
        Set vl_NewQueueNode.nextItem = vl_CurrentQueueNode.cloneNoNext
        Set vl_NewQueueNode = vl_NewQueueNode.nextItem
    Loop
    
    Set vl_Copy = New Queue
    vl_Copy.initWithFront vl_NewFront
    
End If

Set clone = vl_Copy
End Function

Public Function removeElementAndReturnByIndex(indexZeroBased As Integer) As Variant
assignVariantVariable removeElementAndReturnByIndex, getElementByIndexAndDeleteOption(indexZeroBased, True)
End Function

Public Function getElementByIndex(ByVal pr_Index As Integer) As Variant
assignVariantVariable getElementByIndex, getElementByIndexAndDeleteOption(pr_Index, False)
End Function

Private Function getElementByIndexAndDeleteOption(pr_Index As Integer, pr_Delete As Boolean) As Variant
Dim vl_PreviousNode As LLNode
Dim vl_CurrentNode As LLNode

Dim vl_ReturnData As Variant

Dim i As Integer

Const CL_INDEXOOB As Long = 35600

If (pr_Index > vm_numItems_int) Or _
        (pr_Index < 0) Or _
        (vm_numItems_int = 0) Then
    Err.Raise CL_INDEXOOB
End If

If pr_Index = 0 Then
    If pr_Delete Then
        assignVariantVariable vl_ReturnData, dequeue
    Else
        assignVariantVariable vl_ReturnData, vm_Front.Data
    End If
Else
    Set vl_PreviousNode = vm_Front
    Set vl_CurrentNode = vm_Front.nextItem
    
    For i = 0 To pr_Index - 2
        Set vl_PreviousNode = vl_CurrentNode
        Set vl_CurrentNode = vl_CurrentNode.nextItem
    Next i
    
    assignVariantVariable vl_ReturnData, vl_CurrentNode.Data
    
    If pr_Delete Then
        Set vl_PreviousNode.nextItem = vl_CurrentNode.nextItem
        vm_numItems_int = vm_numItems_int - 1
    End If
    
    If pr_Index = vm_numItems_int Then
        Set vm_Rear = vl_PreviousNode
    End If

End If

assignVariantVariable getElementByIndexAndDeleteOption, vl_ReturnData
End Function

Public Function getIterator() As LLIterator
Dim vl_HeadNode As LLNode
Set vl_HeadNode = New LLNode

Dim vl_NewIterator As LLIterator
Set vl_NewIterator = New LLIterator

Dim vl_DataCopy As Queue

Set vl_DataCopy = Me.clone

If vl_DataCopy.numItems = 0 Then
    Set vl_HeadNode = Nothing
Else
    vl_HeadNode.initWithDataAndNext vl_DataCopy.front, vl_DataCopy.frontNode.nextItem
End If
vl_NewIterator.init vl_HeadNode

Set getIterator = vl_NewIterator
End Function


Public Function hasElement(ByVal pr_Element As Variant) As Boolean
Dim vl_CurrentNode As LLNode
Dim vl_Found As Boolean

Set vl_CurrentNode = vm_Front
Do While Not vl_CurrentNode Is Nothing
    If compareVariant(pr_Element, vl_CurrentNode.Data) Then
        vl_Found = True
        Exit Do
    End If
    
    Set vl_CurrentNode = vl_CurrentNode.nextItem
Loop

hasElement = vl_Found
End Function

Public Function removeElementAndReturnSuccess(ByVal pr_Element As Variant) As Boolean
Dim vl_CurrentNode As LLNode
Dim vl_PreviousNode As LLNode

Dim vl_Success As Boolean



If compareVariant(vm_Front.Data, pr_Element) Then
    dequeue
    vl_Success = True
Else
    Set vl_PreviousNode = vm_Front
    Set vl_CurrentNode = vm_Front.nextItem
        
    Do While Not vl_CurrentNode Is Nothing
        If compareVariant(vl_CurrentNode.Data, pr_Element) Then
            'Delete the element
            Set vl_PreviousNode.nextItem = vl_CurrentNode.nextItem
            vm_numItems_int = vm_numItems_int - 1
            vl_Success = True
            Exit Do
        End If
        
        Set vl_PreviousNode = vl_CurrentNode
        Set vl_CurrentNode = vl_CurrentNode.nextItem
    Loop
    
End If

removeElementAndReturnSuccess = vl_Success
End Function


Private Function isVariantInitialized(ByVal pr_Variant As Variant) As Boolean
Dim vl_Return As Boolean


If Not IsEmpty(pr_Variant) Then
    If IsObject(pr_Variant) Then
        If Not pr_Variant Is Nothing Then
            vl_Return = True
        End If
    Else
        vl_Return = True
    End If
End If

isVariantInitialized = vl_Return
End Function
